gin-demo
,这个项目我以后还用到,所以我目录配置齐全一点。go mod init gin-demo
go mod tidy
gin-demo (master) $ tree
.
├── config
├── go.mod
├── go.sum
├── handlers
│ └── IndexController.go
├── main.go
├── middleware
├── model
├── route
│ └── route.go
├── service
│ └── IndexService.go
└── test
在项目中,如果我们代码发生了改动,一般就得手动重新构建,这样显然是很麻烦的。但是我们说过,viper可以用于实现配置的热加载,那么有没有一种办法,可以让go程序也实现热加载呢?
Fresh是一个命令行工具,每次保存Go或模版文件时,该工具都会生成或重新启动Web应用程序。Fresh将监视文件事件,并且每次创建/修改/删除文件时,Fresh都会生成并重新启动应用程序。如果go build返回错误,它会将记录在tmp文件夹中
go get github.com/pilu/fresh
,如果项目在go-path中,则直接执行fresh
命令即可go build github.com/gravityblast/fresh
,这个时候目录下就会生成文件fresh,fresh会启动main文件并监控go程序的改动
fresh -c other_runner.conf
root: .
tmp_path: ./tmp
build_name: runner-build
build_log: runner-build-errors.log
valid_ext: .go, .tpl, .tmpl, .html
no_rebuild_ext: .tpl, .tmpl, .html
ignored: assets, tmp
build_delay: 600
colors: 1
log_color_main: cyan
log_color_build: yellow
log_color_runner: green
log_color_watcher: magenta
log_color_app:
gin run main.go
之后需要先访问3000端口,然后再访问项目端口。相关命令如下# 安装
go get github.com/codegangsta/gin
# 查看帮助
gin help
# 使用 访问一下http://127.0.0.1:3000,再访问http://127.0.0.1:8082
gin run main.go
# 或者使用以下命令启动,访问3000会被代理到8082 这样的话每次只访问3000端口即可
gin -p 3000 -a 8082 -b gin-bin --all run
bee是beego框架的热编译工具,同样可以对GIN框架进行热编译,使用起来很方便,功能也有很多。个人感觉这种热部署用起来更舒服,我们可以通过bee -h
获取更多帮助
项目地址
go get github.com/beego/bee
bee run
bee run -main=app/main.go
package test
import (
"encoding/json"
"fmt"
"gin-demo/handlers"
"github.com/gin-gonic/gin"
"net/http/httptest"
"testing"
)
func TestName(t *testing.T) {
// 创建一个gin.Engine实例
// 创建一个httptest.ResponseRecorder对象
w := httptest.NewRecorder()
// 使用gin.Context的NewContext方法创建一个gin.Context对象
c, _ := gin.CreateTestContext(w)
handlers.Hello(c)
DJ(w.Body.String())
}
viper 是一个配置解决方案,拥有丰富的特性:
viper热加载是利用了第三方库fsnotify.NewWatcher(),用来检测文件,fsnotify利用了os包里面的接口来检测文件是否发生变化,如果发生变化则重新读取。
我们在config文件中创建一个config文件,相关代码如下
package config
import (
"gin-demo/utils"
"github.com/spf13/viper"
"os"
)
func InitConfig() {
# 对应的文件就是 ../etc/dev.yaml
viper.AddConfigPath("../etc")
viper.SetConfigName("dev")
viper.SetConfigType("yaml")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
#监听变化
viper.WatchConfig()
}
这样如果配置文件发生了变化,系统不需要重启就可以生效
在go开发中,高效的日志记录和上下文信息管理是构建可靠和可维护应用的关键方面。 而对于日志而言,我们需求一般需要满足以下几点
zap提供了两种类型的日志记录器Logger 和Sugared Logger 区别是:
比如我们快速入门看一下
func Logger() (log *zap.Logger) {
log, _ = zap.NewProduction()
return log
}
func Hello(c *gin.Context) {
env := viper.GetString("env")
//我们看到,这个Info里面只运行传递string类型的值,但是很明显这种日志记录方式性能更高
utils.Logger().Info(env)
utils.Logger().Info(env)
//如果我们先传递更复杂的值,我们可以使用Sugar,Sugar使用了反射来确定值的类型并记录
utils.SLogger().Info(c)
utils.SLogger().Debugf("%+v", c)
c.JSON(200, env)
}
production日志输出json格式
{"level":"info","ts":1695106856.661728,"caller":"middleware/middleware.go:9","msg":"this is log"}
{"level":"info","ts":1695106856.661864,"caller":"handlers/IndexController.go:11","msg":"self"}
{"level":"info","ts":1695106856.661908,"caller":"handlers/IndexController.go:12","msg":"self"}
development日志输出 行输出
2023-09-19T15:10:29.613+0800 INFO middleware/middleware.go:9 this is log
2023-09-19T15:10:29.613+0800 INFO handlers/IndexController.go:11 self
2023-09-19T15:10:29.613+0800 INFO handlers/IndexController.go:12 self
sugar日志输出
2023-09-19T15:23:17.431+0800 INFO handlers/IndexController.go:13 &{{0xc0006240e0 -1 200} 0xc000490d00 0xc000166100 [] [0x1435b40 0x1436a80 0x143cce0 0x143cb80] 3 /v1/hello 0xc000007380 0xc0000102d0 0xc0000102e8 {{0 0} 0 0 {{} 0} {{} 0}} map[] [] map[] map[] 0}
2023-09-19T15:23:17.431+0800 DEBUG handlers/IndexController.go:14 &{writermem:{ResponseWriter:0xc0006240e0 size:-1 status:200} Request:0xc000490d00 Writer:0xc000166100 Params:[] handlers:[0x1435b40 0x1436a80 0x143cce0 0x143cb80] index:3 fullPath:/v1/hello engine:0xc000007380 params:0xc0000102d0 skippedNodes:0xc0000102e8 mu:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:{_:{} v:0} readerWait:{_:{} v:0}} Keys:map[] Errors: Accepted:[] queryCache:map[] formCache:map[] sameSite:0}
func InitRoute() *gin.Engine {
router := gin.Default()
// 简单组: v1
v1 := router.Group("/v1")
v1.Use(middleware.TraceLog)
{
v1.GET("/hello", handlers.Hello)
}
return router
}
func TraceLog(c *gin.Context) {
guid := xid.New()
requestId := c.Request.Header.Get("X-Request-ID")
if requestId == "" {
//用于程序记录
c.Set("X-Request-ID", guid)
//加到响应头,可以让前端人员记录复用,也便于查找日志
c.Writer.Header().Set("X-Request-ID", guid.String())
}
utils.SLogger().Infof("%+v,%s", c, "this is log")
c.Next()
}
func Hello(c *gin.Context) {
env := viper.GetString("env")
utils.SLogger().Infof("%+v,%s", c, "this is log")
c.JSON(200, env)
}
func TraceLog(c *gin.Context) {
guid := xid.New()
//生成请求id
requestId := c.Request.Header.Get("X-Request-ID")
//设置项目名称
c.Set("appName", "myapp12")
//记录请求时间
startTime := time.Now().Unix()
c.Set("start", startTime)
if requestId == "" {
//用于程序记录
c.Set("X-Request-ID", guid)
//加到响应头,可以让前端人员记录复用,也便于查找日志
c.Writer.Header().Set("X-Request-ID", guid.String())
}
utils.NewLogger(c).Info("this is middleware")
c.Next()
}
func (m MLogger) Info(any any) {
c := m.C
reqId, _ := c.Get("X-Request-ID")
start, _ := c.Get("start")
startI, _ := start.(int64) //转int
startF := time.Unix(startI, 0)
duration := time.Since(startF)
cInfo := fmt.Sprintf("X-Request-ID:%s path:%s startTime:%d duration:%s ", reqId, c.Request.URL, start, duration)
m.SLoggerN.Infof("%s,%+v", cInfo, any)
}
我们开启多个子协程看一下日志效果
func Hello(c *gin.Context) {
env := viper.GetString("env")
wg := sync.WaitGroup{}
wg.Add(3)
go SendMail(&wg, c)
go SendRedis(&wg, c)
go SendMq(&wg, c)
wg.Wait()
utils.NewLogger(c).Info("this is index")
c.JSON(200, env)
}
func SendMail(wg *sync.WaitGroup, c *gin.Context) {
time.Sleep(time.Second * 4)
utils.NewLogger(c).Info("sendMail Over")
wg.Done()
}
func SendRedis(wg *sync.WaitGroup, c *gin.Context) {
time.Sleep(time.Second * 5)
utils.NewLogger(c).Info("sendRedis Over")
wg.Done()
}
func SendMq(wg *sync.WaitGroup, c *gin.Context) {
time.Sleep(time.Second * 6)
utils.NewLogger(c).Info("sendMq Over")
wg.Done()
}
2023-09-20T20:37:39.779+0800 INFO X-Request-ID:ck5ef4vdh7c5qnschdlg path:/v1/hello startTime:1695213459 duration:779.319ms ,this is middleware
2023-09-20T20:37:43.778+0800 INFO X-Request-ID:ck5ef4vdh7c5qnschdlg path:/v1/hello startTime:1695213459 duration:4.778901s ,sendMail Over
2023-09-20T20:37:44.778+0800 INFO X-Request-ID:ck5ef4vdh7c5qnschdlg path:/v1/hello startTime:1695213459 duration:5.778455s ,sendRedis Over
2023-09-20T20:37:45.778+0800 INFO X-Request-ID:ck5ef4vdh7c5qnschdlg path:/v1/hello startTime:1695213459 duration:6.778588s ,sendMq Over
2023-09-20T20:37:45.778+0800 INFO X-Request-ID:ck5ef4vdh7c5qnschdlg path:/v1/hello startTime:1695213459 duration:6.778873s ,this is index